package com.petrooil.web.controller;

import com.petrooil.core.model.controller.ModelUser;
import com.petrooil.core.model.User;
import com.petrooil.core.model.UserDepartment;
import com.petrooil.core.model.UserRole;
import com.petrooil.core.model.session.SessionInfo;
import com.petrooil.core.service.*;
import com.petrooil.utils.SysLookup;
import com.petrooil.web.validator.UserValidator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AnonymousAuthenticationToken;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.LockedException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Controller;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.ui.ModelMap;
import org.springframework.util.StringUtils;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.support.SessionStatus;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.util.HashMap;
import java.util.Map;

@Controller
public class MainController {

    @Autowired
    private IUserService userService;

    @Autowired
    private IRoleUserService userRoleService;

    @Autowired
    private IUserDepartmentService userDepartmentService;

    @Autowired
    private IPetroOilDocService petroOilDocService;

    @Autowired
    UserValidator userValidator;

    private String passwordGenerator(String password) {
        BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
        return passwordEncoder.encode(password);
    }

    @ModelAttribute("rolesMap")
    public Map<String,String> rolesMap() {
        Map<String,String> rolesMap = new HashMap<String, String>();
        rolesMap.put(SysLookup.ROLE.ROLE_USER.toString(), SysLookup.ROLE.ROLE_USER.getDescription());
        rolesMap.put(SysLookup.ROLE.ROLE_ADMIN.toString(), SysLookup.ROLE.ROLE_ADMIN.getDescription());
        rolesMap.put(SysLookup.ROLE.ROLE_FORMS_ADMIN.toString(), SysLookup.ROLE.ROLE_FORMS_ADMIN.getDescription());
        rolesMap.put(SysLookup.ROLE.ROLE_FORMS_DESIGNER.toString(), SysLookup.ROLE.ROLE_FORMS_DESIGNER.getDescription());
        rolesMap.put(SysLookup.ROLE.ROLE_PO_TRASH.toString(), SysLookup.ROLE.ROLE_PO_TRASH.getDescription());
        rolesMap.put(SysLookup.ROLE.ROLE_PO_ARCHIVE.toString(), SysLookup.ROLE.ROLE_PO_ARCHIVE.getDescription());
//        for (SysLookup.ROLE role : SysLookup.ROLE.values()) {
//            rolesMap.put(role.toString(), role.getDescription());
//        }
        return rolesMap;
    }

    @ModelAttribute("departmentsMap")
    public Map<String,String> departmentsMap () {
        Map<String,String> departmentsMap = new HashMap<String, String>();
        for (SysLookup.DEPARTMENT department : SysLookup.DEPARTMENT.values()) {
            departmentsMap.put(department.toString(), department.getDescription());
        }
        departmentsMap.remove(SysLookup.DEPARTMENT.TRASH.toString());
        departmentsMap.remove(SysLookup.DEPARTMENT.ARCHIVE.toString());
        return departmentsMap;
    }

    @RequestMapping(value = {"/admin/addUser" }, method = RequestMethod.GET)
    public ModelAndView addUserView() {
        ModelAndView model = new ModelAndView();
        model.addObject("user", new ModelUser());
        model.setViewName("admin/editUser");
        return model;
    }

    @RequestMapping(value = {"/admin/editUser" }, method = RequestMethod.GET)
    public ModelAndView editUserView(@RequestParam(value="username", required=true) String username) {
        ModelAndView model = new ModelAndView();
        User user = userService.findByUserName(username);
        if (username.isEmpty() || user == null) {
            model.addObject("error", "Niepoprawne dsane");
            model.setViewName("rediract:/admin/userList");
        }

        model.addObject("user", user);
        model.setViewName("admin/editUser");
        return model;
    }

    @RequestMapping(value = {"/admin/removeUser" }, method = RequestMethod.GET)
    public ModelAndView removeUserAction(@RequestParam(value="username", required=true) String username) {
        ModelAndView model = new ModelAndView();
        userService.delete(userService.get(username));
        model.addObject("msg", "Usunięto użytkownika");
        model.setViewName("redirect:/admin/userList");
        return model;
    }

    @RequestMapping(value = {"/admin/saveUser" }, method = RequestMethod.POST)
    public ModelAndView addNewUser(ModelMap model2, @ModelAttribute("user") ModelUser user, BindingResult result, SessionStatus status) {
        ModelAndView model = new ModelAndView();

        userValidator.validate(user, result);

        if (result.hasErrors()) {
            model.addObject("user", user);
            model.setViewName("admin/editUser");
            return model;
        }

        User newUser = new User(user.getLogin(), passwordGenerator(user.getPassword()), true);
        userService.create(newUser);

        for (String role : user.getRoles()) {
            userRoleService.create(new UserRole(newUser, role));
        }

        for (String department : user.getDepartments()) {
            userDepartmentService.create(new UserDepartment(newUser, department));
            userRoleService.create(new UserRole(newUser, "ROLE_PO_" + department));
            break; //TODO na razie obsługuje jeden dział
        }
        model.addObject("msg", "Użytkownika dodano poprawnie");
        model.setViewName("redirect:/admin/userList");
        return model;
    }

	@RequestMapping(value = { "/" }, method = RequestMethod.GET)
	public ModelAndView defaultPage(HttpSession session) {
		ModelAndView model = new ModelAndView();
            model.setViewName("user");
//            model.setViewName("redirect:/updateSession");
		return model;
	}

    @Transactional
    @RequestMapping(value = { "/updateSession" }, method = RequestMethod.GET)
    public ModelAndView updateUserSession(HttpSession session) {
        ModelAndView model = new ModelAndView();
        Authentication auth = SecurityContextHolder.getContext().getAuthentication();
        if (!(auth instanceof AnonymousAuthenticationToken)) {
            UserDetails userDetail = (UserDetails) auth.getPrincipal();
            System.out.println(userDetail);
            model.addObject("username", userDetail.getUsername());
            session.setAttribute(SysLookup.SessionKey.USERNAME, userDetail.getUsername());
            User user = userService.getUserDetails(userDetail.getUsername());
            String department = user.getUserDepartment().iterator().next().getDepartment();
            session.setAttribute(SysLookup.SessionKey.DEPARTMENTS, department);
//            session.setAttribute("department", user.getUserDepartment().toString().replaceAll("[\\[\\] ]", ""));
            StringBuilder sb = new StringBuilder();
            for (UserRole userRole : user.getUserRole()) {
                sb.append(userRole.getRole()).append(",");
            }
            sb.substring(0, sb.length() - 2);
            session.setAttribute(SysLookup.SessionKey.ROLES, sb.toString());
            session.setAttribute(SysLookup.SessionKey.SESSION_INFO, new SessionInfo(petroOilDocService.countTasks(department),
                    SysLookup.DEPARTMENT.valueOf(department).getDescription()));
            model.setViewName("redirect:/");
        } else {
            model.setViewName("redirect:/login");
        }
        return model;
    }

	@RequestMapping(value = "/admin", method = RequestMethod.GET)
	public ModelAndView adminPage() {
		ModelAndView model = new ModelAndView();
		model.setViewName("admin");
		return model;
	}

    @RequestMapping(value = "/add", method = RequestMethod.GET)
    public ModelAndView setParamInSession(HttpSession session) {
        ModelAndView model = new ModelAndView();
        session.setAttribute("department", "testDep");

        model.setViewName("403");
        return model;
    }

	@RequestMapping(value = "/login", method = RequestMethod.GET)
	public ModelAndView login(@RequestParam(value = "error", required = false) String error,
			@RequestParam(value = "logout", required = false) String logout, HttpServletRequest request) {

		ModelAndView model = new ModelAndView();
		if (error != null) {
			model.addObject("error", getErrorMessage(request, "SPRING_SECURITY_LAST_EXCEPTION"));
		}

		if (logout != null) {
			model.addObject("msg", "Zostałeś wylogowany pomyślnie");
		}
		model.setViewName("login");

		return model;
	}

	// customize the error message
	private String getErrorMessage(HttpServletRequest request, String key) {
		Exception exception = (Exception) request.getSession().getAttribute(key);
		String error = "";
		if (exception instanceof BadCredentialsException) {
			error = "Niepoprawny login lub hasło";
		} else if (exception instanceof LockedException) {
			error = exception.getMessage();
		} else {
			error = "Niepoprawny login lub hasło";
		}
		return error;
	}

	// for 403 access denied page
	@RequestMapping(value = "/403", method = RequestMethod.GET)
	public ModelAndView accesssDenied() {
		ModelAndView model = new ModelAndView();

		// check if user is login
		Authentication auth = SecurityContextHolder.getContext().getAuthentication();
		if (!(auth instanceof AnonymousAuthenticationToken)) {
			UserDetails userDetail = (UserDetails) auth.getPrincipal();
			System.out.println(userDetail);
			model.addObject("username", userDetail.getUsername());
		}
		model.setViewName("403");
		return model;
	}

}